package org.reborn.engine.model.mesh

import org.reborn.math._

import scala.math
import scala.collection.mutable.Map

import org.reborn.engine.enums.ArrayType
import org.reborn.engine.enums.FaceMode._

import org.reborn.utils.ResizeableArray

import java.nio.FloatBuffer
import java.nio.IntBuffer
 
class MutableVertexData(private val arrayType:ArrayType.ArrayType,private val pPVertex:Int,private val initVertices:Int,private val resizeAmount:Int) extends VertexData{
  require(resizeAmount>=0)
  
  private var vertData = new ResizeableArray[Float](initVertices * pPVertex,resizeAmount * pPVertex)
  
  def aType():ArrayType.ArrayType = arrayType
  def partsPerVertex():Int = pPVertex
  def verts():Int = vertData.getLength / pPVertex
  def arraySize():Int = vertData.getLength;
  
  def getDataArray() = vertData.getArray
  
  def getDataBuffer() = {
	  val buff = FloatBuffer.wrap(vertData.getArray)
	  buff.limit(vertData.getLength)
	  buff
  }
  
  def apply(position:Int):Float = vertData.getArray(position)
  
  def getVertexAt(vertIndex:Int):VectorF = {
    val startInd = vertIndex * pPVertex
    var partList:List[Float] = Nil
    for(i <- startInd until (startInd + pPVertex) ){ partList = apply(i) :: partList }
    
    VectorF(partList.reverse)
  }
  
  def update(position:Int,value:Float):Unit = vertData.getArray(position) = value
  def update(position:Int,data:Array[Float]):Unit = System.arraycopy(data,0,vertData.getArray,position,data.length)
  
  def append(value:Float):Unit = vertData.appendValue(value)
  def append(data:Array[Float]):Unit = vertData.appendArray(data)
  def append(vertexData:VertexData):Unit = vertData.appendArray(vertData.getArray)
  
  def replace(data:Array[Float]) = {
    vertData = new ResizeableArray(data.length,resizeAmount * pPVertex)
    vertData.appendArray(data)
  }
}

class MutableMesh(private val fMode:FaceMode,private val initialFaceCount:Int,private val resizeAmount:Int) extends Mesh{
  
  private var arrayTypeMap:Map[ArrayType.ArrayType,MutableVertexData] = Map() 
  private var faceData = new ResizeableArray[Int](initialFaceCount * verticesPerFace,resizeAmount * verticesPerFace)
  
  def getAvailableArrayTypes():Iterable[ArrayType.ArrayType] = arrayTypeMap.keySet
  def hasType(arrayType:ArrayType.ArrayType):Boolean = arrayTypeMap.contains(arrayType)
  def apply(arrayType:ArrayType.ArrayType):VertexData = arrayTypeMap(arrayType);
  
  def getFacesArray():Array[Int] = faceData.getArray
  def getFacesBuffer():IntBuffer = {
	  val buff = IntBuffer.wrap(faceData.getArray)
	  buff.limit(faceData.getLength)
	  buff
  }
  
  
  def getFaceMode():FaceMode = fMode
  
  def verts():Int = arrayTypeMap.get(ArrayType.Vertex).getOrElse(return 0).verts
  def faces():Int = faceData.getLength / verticesPerFace
  def verticesPerFace:Int = fMode.id + 1
  
  protected def getTranslatedVertexIndex(faceIndex:Int):Int = faceData.getArray(faceIndex);
  
  def appendVertData(arrayType:ArrayType.ArrayType,partsPerVertex:Int,data:Array[Float]):MutableMesh = {
    val initSize = math.max(initialFaceCount * verticesPerFace, data.length / partsPerVertex)
    val reSize = resizeAmount * verticesPerFace
    if(!hasType(arrayType)) 
      arrayTypeMap += ((arrayType,new MutableVertexData(arrayType,partsPerVertex,initSize,reSize)))
    
    arrayTypeMap(arrayType).append(data)
    this
  }
  def appendFaceData(data:Array[Int]):MutableMesh = {
    faceData.appendArray(data)
    this
  }

  def replaceVertData(arrayType:ArrayType.ArrayType,partsPerVertex:Int,data:Array[Float]):Unit = {
    val initSize = math.max(initialFaceCount * verticesPerFace, data.length / partsPerVertex)
    val reSize = resizeAmount * verticesPerFace
    arrayTypeMap += ((arrayType,new MutableVertexData(arrayType,partsPerVertex,initSize,reSize)))
    
    arrayTypeMap(arrayType).append(data)
  }
  def replaceVertData(arrayType:ArrayType.ArrayType,partsPerVertex:Int,initialVertices:Int):Unit = {
    val reSize = resizeAmount * verticesPerFace
     
    arrayTypeMap += ((arrayType,new MutableVertexData(arrayType,partsPerVertex,initialVertices,reSize)))
  }
  def replaceFaceData(data:Array[Int]):MutableMesh = {
    val initSize = math.max(initialFaceCount, data.length)
    faceData = new ResizeableArray[Int](initSize,resizeAmount)
    faceData.appendArray(data)
    this
  }
  
  def append(mesh:Mesh):MutableMesh = {
    val vertOffset = verts
    
    mesh.getAvailableArrayTypes.foreach((aType) => {
      appendVertData(aType,mesh(aType).partsPerVertex,mesh(aType).getDataArray) 
    })
    
    val offset = faceData.getLength
    val faceArray = mesh.getFacesArray();
    faceData.appendArray(faceArray)
    var i = 0;
    while(i < faceArray.length){
      faceData.getArray(offset + i) = vertOffset + faceArray(i)
      i = i+1
    }
    this
  }
  
  def append(polygon:Polygon):MutableMesh = {
    require(polygon.vertsPerFace == verticesPerFace)
    
    val vertOffset = verts
    val offset = faceData.getLength
    var i = 0;
    
    var aTypeMap:Iterable[ArrayType.ArrayType] = null
    //This is an empty mesh, so initialize it with the data from the
    //polygon.
    if(offset == 0){
    	aTypeMap = polygon.vertices(0).data.keySet 
    }
    else{
    	aTypeMap = getAvailableArrayTypes();
    }
    
    for(vertex <- polygon.vertices){
      for(aType <- aTypeMap){
    	val vec = vertex.data(aType)
    	appendVertData(aType,vec.length,vec.toArray)  
      }
      
      faceData.appendValue(vertOffset + i)
      i+=1
    }
    
    this
  }

}

